package gbase

import (
	"context"
	"fmt"
	"math"
	"math/rand"
	"time"

	"github.com/cloudwego/hertz/pkg/common/hlog"
)

// 重试策略类型
type RetryStrategy int

const (
	// 固定间隔重试 例如 1s,1s,1s,1s,1s
	StrategyFixed RetryStrategy = iota
	// 指数退避重试 例如 1s,2s,4s,8s,16s
	StrategyExponential
	// 线性递增重试 每次增加固定时间 例如 1s,2s,3s,4s,5s
	StrategyLinear
	// 随机间隔重试
	StrategyRandom
)

// 重试配置
type RetryConfig struct {
	MaxAttempts   int                                               // 最大重试次数
	BaseDelay     time.Duration                                     // 基础延迟时间
	MaxDelay      time.Duration                                     // 最大延迟时间,单次延迟最大时间
	Strategy      RetryStrategy                                     // 重试策略
	Multiplier    float64                                           // 指数退避倍数
	Jitter        bool                                              // 是否添加随机抖动
	OnRetry       func(attempt int, err error, delay time.Duration) // 重试回调
	ShouldRetry   func(err error) bool                              // 是否应该重试的判断函数
	EnableLogging bool                                              // 是否启用日志
}

// 重试器
type Retrier struct {
	config RetryConfig
}

// 重试结果
type RetryResult[T any] struct {
	Value    T             // 执行结果
	Err      error         // 最终错误
	Attempts int           // 实际重试次数
	Duration time.Duration // 总耗时
}

// 默认配置
func DefaultRetryConfig() RetryConfig {
	return RetryConfig{
		MaxAttempts:   3,
		BaseDelay:     time.Second,
		MaxDelay:      time.Minute,
		Strategy:      StrategyExponential,
		Multiplier:    2.0,
		Jitter:        true,
		EnableLogging: true,
		ShouldRetry: func(err error) bool {
			// 默认所有错误都重试
			return err != nil
		},
	}
}

// 创建重试器
func NewRetrier(opts ...Option[*RetryConfig]) *Retrier {
	config := DefaultRetryConfig()
	for _, opt := range opts {
		opt(&config)
	}
	return &Retrier{config: config}
}

// 重试器配置选项
func WithMaxAttempts(attempts int) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.MaxAttempts = attempts
	}
}

func WithBaseDelay(delay time.Duration) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.BaseDelay = delay
	}
}

func WithMaxDelay(delay time.Duration) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.MaxDelay = delay
	}
}

func WithStrategy(strategy RetryStrategy) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.Strategy = strategy
	}
}

func WithMultiplier(multiplier float64) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.Multiplier = multiplier
	}
}

func WithJitter(enable bool) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.Jitter = enable
	}
}

func WithOnRetry(callback func(attempt int, err error, delay time.Duration)) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.OnRetry = callback
	}
}

func WithShouldRetry(fn func(err error) bool) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.ShouldRetry = fn
	}
}

func WithLogging(enable bool) Option[*RetryConfig] {
	return func(c *RetryConfig) {
		c.EnableLogging = enable
	}
}

// 执行重试
func (r *Retrier) Do(fn func(attempt int, nextDelay time.Duration) error) error {
	result := r.DoWithResult(func(attempt int, nextDelay time.Duration) (any, error) {
		return nil, fn(attempt, nextDelay)
	})
	return result.Err
}

// 执行重试并返回结果
func (r *Retrier) DoWithResult(fn func(attempt int, nextDelay time.Duration) (any, error)) RetryResult[any] {
	return r.DoWithContext(context.Background(), fn)
}

// 带上下文的重试执行
func (r *Retrier) DoWithContext(ctx context.Context, fn func(attempt int, nextDelay time.Duration) (any, error)) RetryResult[any] {
	var (
		result    RetryResult[any]
		lastErr   error
		startTime = time.Now()
	)

	for attempt := 0; attempt < r.config.MaxAttempts; attempt++ {
		result.Attempts = attempt + 1

		// 检查上下文是否被取消
		select {
		case <-ctx.Done():
			result.Err = ctx.Err()
			result.Duration = time.Since(startTime)
			return result
		default:
		}

		// 计算下次延时（如果这次失败的话）
		var nextDelay time.Duration
		if attempt < r.config.MaxAttempts-1 {
			nextDelay = r.calculateDelay(attempt)
		}

		// 执行函数，传递当前尝试次数和下次延时
		value, err := fn(attempt+1, nextDelay)
		if err == nil {
			result.Value = value
			result.Duration = time.Since(startTime)
			if r.config.EnableLogging && attempt > 0 {
				hlog.Infof("重试成功，尝试次数: %d", attempt+1)
			}
			return result
		}

		lastErr = err

		// 检查是否应该重试
		if !r.config.ShouldRetry(err) {
			if r.config.EnableLogging {
				hlog.Warnf("错误不可重试: %v", err)
			}
			result.Err = err
			result.Duration = time.Since(startTime)
			return result
		}

		// 如果不是最后一次尝试，等待后重试
		if attempt < r.config.MaxAttempts-1 {
			delay := nextDelay

			if r.config.EnableLogging {
				hlog.Warnf("第 %d 次尝试失败: %v，%v 后重试", attempt+1, err, delay)
			}

			// 调用重试回调
			if r.config.OnRetry != nil {
				r.config.OnRetry(attempt+1, err, delay)
			}

			// 等待延迟
			select {
			case <-ctx.Done():
				result.Err = ctx.Err()
				result.Duration = time.Since(startTime)
				return result
			case <-time.After(delay):
			}
		}
	}

	result.Err = fmt.Errorf("重试 %d 次后仍然失败: %w", r.config.MaxAttempts, lastErr)
	result.Duration = time.Since(startTime)

	if r.config.EnableLogging {
		hlog.Errorf("重试失败: %v", result.Err)
	}

	return result
}

// 计算延迟时间
func (r *Retrier) calculateDelay(attempt int) time.Duration {
	var delay time.Duration

	switch r.config.Strategy {
	case StrategyFixed:
		delay = r.config.BaseDelay

	case StrategyExponential:
		delay = time.Duration(float64(r.config.BaseDelay) * math.Pow(r.config.Multiplier, float64(attempt)))

	case StrategyLinear:
		delay = r.config.BaseDelay * time.Duration(attempt+1)

	case StrategyRandom:
		delay = time.Duration(rand.Int63n(int64(r.config.BaseDelay)*2)) + r.config.BaseDelay/2

	default:
		delay = r.config.BaseDelay
	}

	// 添加随机抖动
	if r.config.Jitter {
		jitter := time.Duration(rand.Float64() * float64(delay) * 0.1)
		delay += jitter
	}

	// 限制最大延迟
	if delay > r.config.MaxDelay {
		delay = r.config.MaxDelay
	}

	return delay
}

// 便捷函数：简单重试,每秒执行一次
func Retry(fn func(attempt int, nextDelay time.Duration) error, maxAttempts int) error {
	retrier := NewRetrier(
		WithMaxAttempts(maxAttempts),
		WithStrategy(StrategyFixed),
	)
	return retrier.Do(fn)
}

// 便捷函数：按指定延迟的重试
func RetryWithDelay(fn func(attempt int, nextDelay time.Duration) error, maxAttempts int, delay time.Duration) error {
	retrier := NewRetrier(
		WithMaxAttempts(maxAttempts),
		WithBaseDelay(delay),
		WithStrategy(StrategyFixed),
	)
	return retrier.Do(fn)
}

// 便捷函数：指数退避重试
func RetryWithExponentialBackoff(fn func(attempt int, nextDelay time.Duration) error, maxAttempts int, baseDelay time.Duration) error {
	retrier := NewRetrier(
		WithMaxAttempts(maxAttempts),
		WithBaseDelay(baseDelay),
		WithStrategy(StrategyExponential),
	)
	return retrier.Do(fn)
}

// 常见错误判断函数
var (
	// 网络错误重试
	ShouldRetryNetworkError = func(err error) bool {
		if err == nil {
			return false
		}
		// 这里可以根据具体的错误类型判断
		errStr := err.Error()
		return contains(errStr, "connection refused") ||
			contains(errStr, "timeout") ||
			contains(errStr, "network") ||
			contains(errStr, "dial")
	}

	// HTTP错误重试（5xx状态码）
	ShouldRetryHTTPError = func(err error) bool {
		if err == nil {
			return false
		}
		// 这里可以根据HTTP状态码判断
		errStr := err.Error()
		return contains(errStr, "500") ||
			contains(errStr, "502") ||
			contains(errStr, "503") ||
			contains(errStr, "504")
	}
)

// 辅助函数
func contains(s, substr string) bool {
	return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
		(len(s) > len(substr) && s[:len(substr)] == substr) ||
		(len(s) > len(substr) && s[len(s)-len(substr):] == substr) ||
		findInString(s, substr))
}

func findInString(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}
	return false
}
